package com.wzdigit.wms.hm.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wzdigit.framework.Shift;
import com.wzdigit.wms.hm.client.resp.InvQtyAndOutQtyResponse;
import com.wzdigit.wms.hm.config.StatusCode;
import com.wzdigit.wms.hm.domain.*;
import com.wzdigit.wms.hm.mapper.InvLocMapper;
import com.wzdigit.wms.hm.mapper.InvTransMapper;
import com.wzdigit.wms.hm.mapper.InvZoneMapper;
import com.wzdigit.wms.hm.mapper.LocationMapper;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

/**
 * 库存服务
 */
@Service
@Slf4j
public class InventoryService {

    @Autowired
    private InvLocMapper invLocMapper;

    @Autowired
    private InvTransMapper invTransMapper;

    @Autowired
    private InvZoneMapper invZoneMapper;

    @Autowired
    private LocationMapper locationMapper;

    @Autowired
    private RedissonClient redisson;
    
//    private static final Logger logger = LoggerFactory.getLogger(LogHandlerInterceptor.class);


    /**
     * 库存增加   null -> toInv
     *
     * @param toInv
     * @param invTrans
     */
    public void inventoryIn(UpdateLocInventoryInputDto toInv, InvTrans invTrans) {
        this.inventoryTransfer(null, toInv, invTrans);
    }

    /**
     * 库存减少  fmInv -> null
     *
     * @param fmInv
     * @param invTrans
     */
    public void inventoryOut(UpdateLocInventoryInputDto fmInv, InvTrans invTrans) {
        this.inventoryTransfer(fmInv, null, invTrans);
    }

    /**
     * 库存调拨   fmInv -> toInv
     *
     * @param fmInv
     * @param toInv
     * @param invTrans
     */
    public void inventoryTransfer(UpdateLocInventoryInputDto fmInv, UpdateLocInventoryInputDto toInv, InvTrans invTrans) {

        if (fmInv != null) {
        	
            /*if (fmInv.getQty().compareTo(BigDecimal.ZERO) < 0) {
                boolean isPreQty = fmInv.getPreOutQty().compareTo(BigDecimal.ZERO) < 0;
                if (!this.locQtyValidation(fmInv, fmInv.getQty().abs(), isPreQty)) {
                    Shift.fatal(StatusCode.BUSINESS_ERR, "可用库存不足!");
                }
            }
            if (fmInv.getLocId() != null && fmInv.getLocId() != 0 && fmInv.getZoneId() == null) {
                Location location = this.locationMapper.selectById(fmInv.getLocId());
                fmInv.setZoneId(location == null ? null : location.getZoneId());
            }
            if (fmInv.getQty().compareTo(BigDecimal.ZERO) < 0 || fmInv.getPreAlocQty().compareTo(BigDecimal.ZERO) < 0) {
                if (!this.zoneQtyValidation(fmInv, fmInv.getPreAlocQty().abs())) {
                    Shift.fatal(StatusCode.BUSINESS_ERR, "库存不足,无法进行预分配!");
//                    Shift.fatal("库存不足,无法进行预分配!");
                }
            }*/

            // 库存校验
            this.invQtyValidation(fmInv);

            this.updateLocInventroy(fmInv);
            this.updateZoneInventory(fmInv);
            invTrans.setFmOwner(fmInv.getOwner());
            invTrans.setFmSkuId(fmInv.getSkuId());
            invTrans.setFmFactory(fmInv.getFactory());
            invTrans.setFmLotno(fmInv.getLotNo());
            invTrans.setFmVendorId(fmInv.getVendorId());
            invTrans.setFmLocId(fmInv.getLocId());
            invTrans.setFmWhsId(fmInv.getWhsId());
            invTrans.setFmInvAttr(fmInv.getInvAttr());
            invTrans.setFmQaStatus(fmInv.getQaStatus());
            invTrans.setFmUnit(fmInv.getUnit());
            invTrans.setFmQty(fmInv.getQty());
            invTrans.setFmPreAlocQty(fmInv.getPreAlocQty());
            invTrans.setFmPreInQty(fmInv.getPreInQty());
            invTrans.setFmPreOutQty(fmInv.getPreOutQty());
            invTrans.setFmHoldQty(fmInv.getHoldQty());
            invTrans.setFmAlocQty(fmInv.getAlocQty());

            invTrans.setCreateUser(fmInv.getOperator());
            invTrans.setModifyUser(fmInv.getOperator());
        }
        if (toInv != null) {
            this.updateLocInventroy(toInv);
            this.updateZoneInventory(toInv);
            invTrans.setToOwner(toInv.getOwner());
            invTrans.setToSkuId(toInv.getSkuId());
            invTrans.setToFactory(toInv.getFactory());
            invTrans.setToLotno(toInv.getLotNo());
            invTrans.setToVendorId(toInv.getVendorId());
            invTrans.setToLocId(toInv.getLocId());
            invTrans.setToWhsId(toInv.getWhsId());
            invTrans.setToInvAttr(toInv.getInvAttr());
            invTrans.setToQaStatus(toInv.getQaStatus());
            invTrans.setToUnit(toInv.getUnit());

            invTrans.setToQty(toInv.getQty());
            invTrans.setToPreAlocQty(toInv.getPreAlocQty());
            invTrans.setToPreInQty(toInv.getPreInQty());
            invTrans.setToPreOutQty(toInv.getPreOutQty());
            invTrans.setToHoldQty(toInv.getHoldQty());
            invTrans.setToAlocQty(toInv.getAlocQty());

            invTrans.setCreateUser(toInv.getOperator());
            invTrans.setModifyUser(toInv.getOperator());
        }
        invTrans.setCreateTime(new Date());
        invTrans.setModifyTime(new Date());
        this.insertInvTrans(invTrans);
    }

    /**
     * 获取库位库存行ID
     *
     * @param input
     * @return
     */
    private Integer getLocInvId(UpdateLocInventoryInputDto input) {
        QueryWrapper<InvLoc> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("INV_LOC_ID");
        if (input.getLocId() == null) {
            queryWrapper.isNull("LOC_ID");
        } else {
            //获取库位
            queryWrapper.eq("LOC_ID", input.getLocId());
        }
        queryWrapper.eq("WHS_ID", input.getWhsId());
        queryWrapper.eq("OWNER", input.getOwner());
        queryWrapper.eq("SKU_ID", input.getSkuId());

        queryWrapper.isNull(input.getFactory() == null, "FACTORY");
        queryWrapper.eq(!(input.getFactory() == null), "FACTORY", input.getFactory());

        queryWrapper.isNull(input.getVendorId() == null, "VENDOR_ID");
        queryWrapper.eq(!(input.getVendorId() == null), "VENDOR_ID", input.getVendorId());

        //queryWrapper.eq("LOTNO", input.getLotNo());
        queryWrapper.isNull(input.getLotNo() == null, "LOTNO");
        queryWrapper.eq(!(input.getLotNo() == null), "LOTNO", input.getLotNo());


        queryWrapper.isNull(input.getInvAttr() == null, "INV_ATTR");
        queryWrapper.eq(!(input.getInvAttr() == null), "INV_ATTR", input.getInvAttr());

        //TODO 添加非空校验，不知对程序有没印象
//        if (input.getQaStatus() != null) {
//            queryWrapper.eq("QA_STATUS", input.getQaStatus());
//        }

        queryWrapper.isNull(input.getQaStatus() == null, "QA_STATUS");
        queryWrapper.eq(!(input.getQaStatus() == null), "QA_STATUS", input.getQaStatus());

        List<InvLoc> list = this.invLocMapper.selectList(queryWrapper);
        if (list.size() == 0)
            return 0;
        return list.get(0).getInvLocId();
    }

    /**
     * 验证库存是否 充足
     *
     * @param input
     * @param qty
     * @return
     */
    public boolean locQtyValidation(InventoryConditionDto input, BigDecimal qty) {
        return this.locQtyValidation(input, qty, false);
    }

    /**
     * 校验库存
     *
     * @param dto
     */
    public void invQtyValidation(UpdateLocInventoryInputDto dto) {
        SearchInvRequest searchInvRequest = new SearchInvRequest();
        searchInvRequest.setWhsId(dto.getWhsId());
        searchInvRequest.setOwnerId(dto.getOwner());
        searchInvRequest.setSkuId(dto.getSkuId());
        searchInvRequest.setFactory(dto.getFactory());
        searchInvRequest.setVendorId(dto.getVendorId());
        searchInvRequest.setLotno(dto.getLotNo());
        searchInvRequest.setInvAttr(dto.getInvAttr());
        searchInvRequest.setQaStatus(dto.getQaStatus());

        // 数量
        BigDecimal qty = dto.getQty();
        // 支出数量（预支数量 + 冻结数量 + 预配数量 + 分配数量）
        BigDecimal outQty = dto.getPreOutQty().add(dto.getHoldQty()).add(dto.getPreAlocQty()).add(dto.getAlocQty());

        List<InvQtyAndOutQtyResponse> qtyAndOutQty = null;
        if (dto.getLocId() != null) {
            // 查库位库存
            searchInvRequest.setLocId(dto.getLocId());
            qtyAndOutQty = this.invLocMapper.getQtyAndOutQty(searchInvRequest);
        } else {
            // 查库区库存
            searchInvRequest.setZoneId(dto.getZoneId());
            qtyAndOutQty = this.invZoneMapper.getQtyAndOutQty(searchInvRequest);
        }

        log.info("库存校验【入参】：数量=" + qty + ";支出=" + outQty);
        int i = 0;
        for (InvQtyAndOutQtyResponse one : qtyAndOutQty) {
            log.info("库存校验【现存" + (++i) + "】：数量=" + one.getQty() + ";支出=" + one.getOutQty());
            qty = qty.add(one.getQty());
            outQty = outQty.add(one.getOutQty());
        }
        // 数量小于支出数量：可用库存变负数
        log.info("库存校验【结果】：数量=" + qty + ";支出=" + outQty);
        if (qty.signum() == -1 || outQty.signum() == -1 || qty.compareTo(outQty) == -1) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "库存不足!");
        }
    }

    /**
     * 验证库存是否 充足
     *
     * @param input
     * @param qty
     * @param isSubPre false：添加预支校验
     * @return
     */
    public boolean locQtyValidation(InventoryConditionDto input, BigDecimal qty, boolean isSubPre) {
        QueryWrapper<InvLoc> queryWrapper = new QueryWrapper<>();
        if (input.getLocId() == null) {
            queryWrapper.isNull("LOC_ID");
        } else {
            //获取库位
            queryWrapper.eq("LOC_ID", input.getLocId());
        }
        queryWrapper.eq("WHS_ID", input.getWhsId());
        queryWrapper.eq("OWNER", input.getOwner());
        queryWrapper.eq("SKU_ID", input.getSkuId());

        queryWrapper.isNull(input.getFactory() == null, "FACTORY");
        queryWrapper.eq(!(input.getFactory() == null), "FACTORY", input.getFactory());

        queryWrapper.isNull(input.getVendorId() == null, "VENDOR_ID");
        queryWrapper.eq(!(input.getVendorId() == null), "VENDOR_ID", input.getVendorId());

        queryWrapper.eq("LOTNO", input.getLotNo());

        queryWrapper.isNull(input.getInvAttr() == null, "INV_ATTR");
        queryWrapper.eq(!(input.getInvAttr() == null), "INV_ATTR", input.getInvAttr());

        //TODO 添加非空校验，不知对程序有没印象
        if (input.getQaStatus() != null) {
            queryWrapper.eq("QA_STATUS", input.getQaStatus());
        }

        List<InvLoc> invLocs = this.invLocMapper.selectList(queryWrapper);

        if (invLocs.size() == 0)
            return false;
        BigDecimal nowQty;
        if (isSubPre) {
            nowQty = invLocs.stream().map(t ->
                    t.getQty().subtract(t.getHoldQty()).subtract(t.getAlocQty())
            ).reduce(BigDecimal.ZERO, BigDecimal::add);
        } else {
            nowQty = invLocs.stream().map(t ->
                    t.getQty().subtract(t.getPreOutQty()).subtract(t.getHoldQty()).subtract(t.getAlocQty())
            ).reduce(BigDecimal.ZERO, BigDecimal::add);
        }
        if (nowQty.compareTo(qty) >= 0)
            return true;
        Shift.fatal("可用库存不足，需求[" + qty + "]，可用库存[" + nowQty + "]");
        return false;
    }

    /**
     * 预分配数量校验!
     *
     * @param input  条件
     * @param preQty 即将分配数量
     * @return
     */
    public boolean zoneQtyValidation(InventoryConditionDto input, BigDecimal preQty) {
        QueryWrapper<InvZone> queryWrapper = new QueryWrapper<>();
        if (input.getZoneId() == null) {
            queryWrapper.isNull("ZONE_ID");
        } else {
            //获取库区
            queryWrapper.eq("ZONE_ID", input.getZoneId());
        }
        queryWrapper.eq("WHS_ID", input.getWhsId());
        queryWrapper.eq("OWNER", input.getOwner());
        queryWrapper.eq("SKU_ID", input.getSkuId());

        queryWrapper.isNull(input.getFactory() == null, "FACTORY");
        queryWrapper.eq(!(input.getFactory() == null), "FACTORY", input.getFactory());

        queryWrapper.isNull(input.getVendorId() == null, "VENDOR_ID");
        queryWrapper.eq(!(input.getVendorId() == null), "VENDOR_ID", input.getVendorId());

        queryWrapper.isNull(input.getLotNo() == null, "LOTNO");
        queryWrapper.eq(!(input.getLotNo() == null), "LOTNO", input.getLotNo());

        queryWrapper.isNull(input.getInvAttr() == null, "INV_ATTR");
        queryWrapper.eq(!(input.getInvAttr() == null), "INV_ATTR", input.getInvAttr());

        queryWrapper.isNull(input.getQaStatus() == null, "QA_STATUS");
        queryWrapper.eq(!(input.getQaStatus() == null), "QA_STATUS", input.getQaStatus());

        List<InvZone> invZones = this.invZoneMapper.selectList(queryWrapper);
        if (invZones.size() == 0)
            return false;
        BigDecimal nowPreQty = invZones.stream().map(t ->
                t.getQty().subtract(t.getHoldQty()).subtract(t.getPreAlocQty()).subtract(t.getAlocQty()).subtract(t.getPreOutQty())
        ).reduce(BigDecimal.ZERO, BigDecimal::add);

//        return nowPreQty.compareTo(preQty) >= 0;
        return nowPreQty.compareTo(preQty) > 0;
    }


    /**
     * 更新库存
     *
     * @param input
     */
    public Integer updateLocInventroy(UpdateLocInventoryInputDto input) {
        Integer itemId = this.getOrAddLocInvItem(input);
        log.info("库位库存ID:{}", itemId);
        this.invLocMapper.updateQtyById(itemId,
                input.getQty(),
                input.getHoldQty(),
                input.getAlocQty(),
                input.getPreInQty(),
                input.getPreOutQty(),
                input.getPreAlocQty(),
                input.getOperator());

        //创建类型的条件构造器
        QueryWrapper<InvLoc> wrapper = new QueryWrapper<>();
        wrapper.eq("INV_LOC_ID", itemId);
        InvLoc invLoc = invLocMapper.selectOne(wrapper);
        System.out.println(invLoc);

        return itemId;
    }

//    public void updateInvZoneQty(UpdateInvZoneQtyDto input) {
//        this.invZoneMapper.updateQtyById(
//                input.getInvZoneId(),
//                input.getQty(),
//                input.getHoldQty(),
//                input.getAlocQty(),
//                input.getPreInQty(),
//                input.getPreOutQty(),
//                input.getPreAlocQty(),
//                input.getOperator());
//    }

    public Integer updateZoneInventory(UpdateLocInventoryInputDto input) {

        Integer itemId = this.getOrAddZoneItem(input);
        log.info(input.toString());
        log.info("库区库存ID:{}", itemId);

        this.invZoneMapper.updateQtyById(
                itemId,
                input.getQty(),
                input.getHoldQty(),
                input.getAlocQty(),
                input.getPreInQty(),
                input.getPreOutQty(),
                input.getPreAlocQty(),
                input.getOperator());

        //创建类型的条件构造器
        QueryWrapper<InvZone> wrapper = new QueryWrapper<>();
        wrapper.eq("INV_ZONE_ID", itemId);
        InvZone invZone = invZoneMapper.selectOne(wrapper);
        System.out.println(invZone);
        return itemId;
    }

    /**
     * 获取库区库存
     *
     * @param input
     * @return
     */
    private Integer getZoneInvId(UpdateLocInventoryInputDto input) {
        QueryWrapper<InvZone> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("INV_ZONE_ID");
        if (input.getZoneId() == null) {
            queryWrapper.isNull("ZONE_ID");
        } else {
            //获取库区
            queryWrapper.eq("ZONE_ID", input.getZoneId());
        }
        queryWrapper.eq("WHS_ID", input.getWhsId());
        queryWrapper.eq("OWNER", input.getOwner());
        queryWrapper.eq("SKU_ID", input.getSkuId());

        queryWrapper.isNull(input.getFactory() == null, "FACTORY");
        queryWrapper.eq(!(input.getFactory() == null), "FACTORY", input.getFactory());

        queryWrapper.isNull(input.getVendorId() == null, "VENDOR_ID");
        queryWrapper.eq(!(input.getVendorId() == null), "VENDOR_ID", input.getVendorId());

        //queryWrapper.eq("LOTNO", input.getLotNo());
        queryWrapper.isNull(input.getLotNo() == null, "LOTNO");
        queryWrapper.eq(!(input.getLotNo() == null), "LOTNO", input.getLotNo());

        queryWrapper.isNull(input.getInvAttr() == null, "INV_ATTR");
        queryWrapper.eq(!(input.getInvAttr() == null), "INV_ATTR", input.getInvAttr());

//        if (input.getQaStatus() != null) {
//            queryWrapper.eq("QA_STATUS", input.getQaStatus());
//        }

        queryWrapper.isNull(input.getQaStatus() == null, "QA_STATUS");
        queryWrapper.eq(!(input.getQaStatus() == null), "QA_STATUS", input.getQaStatus());

        List<InvZone> list = this.invZoneMapper.selectList(queryWrapper);
        if (list.size() == 0)
            return 0;
        return list.get(0).getInvZoneId();
    }

    /**
     * 获取ID, 不存在则新增一行
     *
     * @param input
     * @return
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public Integer getOrAddLocInvItem(UpdateLocInventoryInputDto input) {

        String key = "Key_Inv_Loc_"
                + input.getWhsId() + "#loc" + input.getLocId() + "#" + input.getOwner() + "#"
                + input.getSkuId() + "#" + input.getFactory() + "#" + input.getVendorId() + "#"
                + input.getLotNo() + "#" + input.getInvAttr() + "#" + input.getQaStatus() + "#";

        RLock lock = redisson.getLock(key);
        Integer itemId = 0;
        try {
            boolean status = lock.tryLock();
            if (status) {
                itemId = this.getLocInvId(input);
                if (itemId == 0) {
                    InvLoc item = new InvLoc();

                    item.setLocId(input.getLocId());
                    item.setWhsId(input.getWhsId());
                    item.setOwner(input.getOwner());
                    item.setSkuId(input.getSkuId());
                    item.setFactory(input.getFactory());
                    item.setVendorId(input.getVendorId());
                    item.setLotno(input.getLotNo());
                    item.setInvAttr(input.getInvAttr());
                    item.setQaStatus(input.getQaStatus());


                    item.setUnit(input.getUnit());

                    item.setCreateTime(new Date());
                    item.setModifyTime(new Date());
                    item.setCreateUser(input.getOperator());
                    item.setModifyUser(input.getOperator());
                    this.invLocMapper.insert(item);

                    itemId = item.getInvLocId();
                }
            }
        } finally {
            // 当前线程获取到锁再释放锁
            if (lock != null && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return itemId;
    }

    /**
     * 生成库区库存
     *
     * @param input
     * @return
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public Integer getOrAddZoneItem(UpdateLocInventoryInputDto input) {

        if (input.getLocId() != null && input.getLocId() != 0 && input.getZoneId() == null) {
            Location location = this.locationMapper.selectById(input.getLocId());
            input.setZoneId(location == null ? null : location.getZoneId());
        }

        String key = "Key_Inv_Zone_"
                + input.getWhsId() + "#zone" + input.getZoneId() + "#" + input.getOwner() + "#"
                + input.getSkuId() + "#" + input.getFactory() + "#" + input.getVendorId() + "#"
                + input.getLotNo() + "#" + input.getInvAttr() + "#" + input.getQaStatus() + "#";
        RLock lock = redisson.getLock(key);
        Integer itemId = 0;
        try {
            boolean status = lock.tryLock();
            if (status) {
                log.info("input.getLocId():", input.getLocId());
                itemId = this.getZoneInvId(input);
                if (itemId == 0) {
                    InvZone item = new InvZone();
                    item.setZoneId(input.getZoneId());
                    item.setWhsId(input.getWhsId());
                    item.setOwner(input.getOwner());
                    item.setSkuId(input.getSkuId());
                    item.setFactory(input.getFactory());
                    item.setVendorId(input.getVendorId());
                    item.setLotno(input.getLotNo());
                    item.setInvAttr(input.getInvAttr());
                    item.setQaStatus(input.getQaStatus());

                    item.setUnit(input.getUnit());

                    item.setCreateTime(new Date());
                    item.setModifyTime(new Date());
                    item.setCreateUser(input.getOperator());
                    item.setModifyUser(input.getOperator());
                    this.invZoneMapper.insert(item);
                    itemId = item.getInvZoneId();
                }
            }
        } finally {
            // 当前线程获取到锁再释放锁
            if (lock != null && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return itemId;
    }

    /**
     * 添加日志事务!
     *
     * @param input
     * @return
     */
    public void insertInvTrans(InvTrans input) {
        int cnt = this.invTransMapper.insert(input);
        if (cnt == 0) {
            log.debug("添加事务日志失败!");
        }
    }


    /**
     * 返回可用库存,一键调拨专用，抄LHQ的方法
     *
     * @param input
     * @return
     */
    public BigDecimal getLocQtyValidation(InventoryConditionDto input) {
        QueryWrapper<InvZone> queryWrapper = new QueryWrapper<>();
//        if (input.getZoneId() == null) {
//            queryWrapper.isNull("ZONE_ID");
//        } else {
//            //获取库位
//            queryWrapper.eq("ZONE_ID", input.getZoneId());
//        }
        queryWrapper.eq("WHS_ID", input.getWhsId());
        queryWrapper.eq("OWNER", input.getOwner());
        queryWrapper.eq("SKU_ID", input.getSkuId());

        queryWrapper.isNull(input.getFactory() == null, "FACTORY");
        queryWrapper.eq(!(input.getFactory() == null), "FACTORY", input.getFactory());

        queryWrapper.isNull(input.getVendorId() == null, "VENDOR_ID");
        queryWrapper.eq(!(input.getVendorId() == null), "VENDOR_ID", input.getVendorId());

        queryWrapper.eq("LOTNO", input.getLotNo());

        queryWrapper.isNull(input.getInvAttr() == null, "INV_ATTR");
        queryWrapper.eq(!(input.getInvAttr() == null), "INV_ATTR", input.getInvAttr());

        //TODO 添加非空校验，不知对程序有没印象
        if (input.getQaStatus() != null) {
            queryWrapper.eq("QA_STATUS", input.getQaStatus());
        }

        List<InvZone> invZones = this.invZoneMapper.selectList(queryWrapper);

        if (invZones.size() == 0)
            return BigDecimal.ZERO;

        BigDecimal nowQty = invZones.stream().map(t ->
                t.getQty().subtract(t.getHoldQty()).subtract(t.getAlocQty()).subtract(t.getPreAlocQty())
        ).reduce(BigDecimal.ZERO, BigDecimal::add);

        return nowQty;
    }

}
